/**
	The Minerva Project, DAG Scheduling Tools
	Copyright (C) 2006 Florin Pop & Stefan Bucur

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 */

package ro.pub.cs.minerva.resource;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @author Florin Pop
 *
 */
public class ResourcesConfiguration {
	
	private String configurationFile;
	private HashMap<String, Resource> resourceList;
	
	/**
	 * 
	 *
	 */
	public ResourcesConfiguration() {
		this.configurationFile = "Resources.xml";
		this.resourceList = new HashMap<String, Resource>();
		this.getResourcesConfiguration();
	}
	
	/**
	 * 
	 * @param configurationFile
	 */
	public ResourcesConfiguration(String configurationFile) {
		this.configurationFile = configurationFile;
		this.resourceList = new HashMap<String, Resource>();
		this.getResourcesConfiguration();
	}
	
	/**
	 * @param configurationFile
	 * @param resourceList
	 */
	public ResourcesConfiguration(String configurationFile, HashMap<String, Resource> resourceList) {
		this.configurationFile = configurationFile;
		this.resourceList = resourceList;
	}

	/**
	 * @return the configurationFile
	 */
	public String getConfigurationFile() {
		return configurationFile;
	}

	/**
	 * @param configurationFile the configurationFile to set
	 */
	public void setConfigurationFile(String configurationFile) {
		this.configurationFile = configurationFile;
	}

	/**
	 * @return the resourceList
	 */
	public HashMap<String, Resource> getResourceList() {
		return resourceList;
	}

	/**
	 * @param resourceList the resourceList to set
	 */
	public void setResourceList(HashMap<String, Resource> resourceList) {
		this.resourceList = resourceList;
	}

	private Document parseXMLFile() {
		
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setIgnoringElementContentWhitespace(true);
		
		DocumentBuilder db;
		Document document = null;
		
		// try to create a new document builder
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException pce) {
			System.err.println("Wrong parser configuration: " + pce.getMessage());
			return null;
		}
		
		File xmlFile = new File(configurationFile);
		
		// try to parse xml file
		try {
			document = db.parse(xmlFile);
		} catch (SAXException saxe) {
			System.err.println("Wrong XML file structure: " + saxe.getMessage());
			return null;
		}
		catch (IOException ioe) {
		    System.out.println("Could not read source file: " + ioe.getMessage());
		}
		
		return document;
	}
	
	private void getResourcesConfiguration() {
		// parse XML file -> XML document will be build
		Document document = parseXMLFile();
		
		// get root node of xml tree structure
		Node root = document.getDocumentElement();
		if (root == null) {
        	throw new RuntimeException("Error at parsing XML document...");
        }
        
		this.resourceList = getResourcesFromDocument(root);
	}
	
	private HashMap<String, Resource> getResourcesFromDocument(Node root) {
		
		HashMap<String, Resource> resourceList = new HashMap<String, Resource>();
		
		String rootName = root.getNodeName();
		if(!rootName.equals("Configuration")) {
			return resourceList; // empty list
		} else {
			try {
				NodeList nodeList = root.getChildNodes();
				Node parent;
				for (int i = 1; i < nodeList.getLength(); i+=2) {
					
					Resource resource = new Resource();
					
					parent = nodeList.item(i);
					if ( (parent.getNodeType() == Node.ELEMENT_NODE) && ((parent.getNodeName()).equals("Node"))) {
						
						int k = 1;
						
						NodeList children = parent.getChildNodes();
						Node child = children.item(k);
						
						if ((child.getNodeType() == Node.ELEMENT_NODE) && ((child.getNodeName()).equals("Id"))){
							resource.setProcId(getElementValue(child));
						}
						
						k = k + 2;
						child = children.item(k);
						if ((child.getNodeType() == Node.ELEMENT_NODE) && ((child.getNodeName()).equals("FarmName"))){
							resource.setFarmName(getElementValue(child));
						}
						
						k = k + 2;
						child = children.item(k);
						if ((child.getNodeType() == Node.ELEMENT_NODE) && ((child.getNodeName()).equals("ClusterName"))){
							resource.setClusterName(getElementValue(child));
						}
						
						k = k + 2;
						child = children.item(k);
						if ((child.getNodeType() == Node.ELEMENT_NODE) && ((child.getNodeName()).equals("NodeName"))){
							resource.setNodeName(getElementValue(child));
						}
						
						k = k + 2;
						child = children.item(k);
						if ((child.getNodeType() == Node.ELEMENT_NODE) && ((child.getNodeName()).equals("Parameters"))) {
							ResourceParameters resourceParameters = new ResourceParameters();
							
							NodeList nephews = child.getChildNodes();
							Node nephew;
							
							for (int j = 1; j < nephews.getLength(); j+=2) {
								nephew = nephews.item(j);
								
								if ((nephew.getNodeType() == Node.ELEMENT_NODE) && ((nephew.getNodeName()).equals("CPUPower"))){
									String ev = getElementValue(nephew);
									double cpuPower = 0D;
									int index;
									if ((((index = ev.indexOf("MHZ"))) != -1) && ev.endsWith("MHZ"))
										cpuPower = (new Double(ev.substring(0,index))).doubleValue();
									resourceParameters.setCpuPower(cpuPower);
								} else if ((nephew.getNodeType() == Node.ELEMENT_NODE) && ((nephew.getNodeName()).equals("Memory"))){
									String ev = getElementValue(nephew);
									double memory = 0D;
									int index;
									if ((((index = ev.indexOf("MB"))) != -1) && ev.endsWith("MB"))
										memory = (new Double(ev.substring(0,index))).doubleValue();
									resourceParameters.setMemory(memory);
								}
								else if ((nephew.getNodeType() == Node.ELEMENT_NODE) && ((nephew.getNodeName()).equals("CPU_idle"))){
									resourceParameters.setCpuIdle(new Double(getElementValue(nephew)).doubleValue());
								}
							}
							
							resource.setParameters(resourceParameters);
						}
					}
					resourceList.put(resource.getProcId(), resource);
				}
			} catch (Exception e) {
				System.err.println("Error at parsing XML document..." + e.getMessage());
				return null;
			}
		}
		
		return resourceList;
	}
	  
	/**
	 * method which returns element value from an XML file;
	 * receives as a parameter the element from the XML file
	 * @param elem 
	 */
	private final static String getElementValue( Node elem ) {
		Node kid;
		if( elem != null ){
			if (elem.hasChildNodes()){
				for( kid = elem.getFirstChild(); kid != null; kid = kid.getNextSibling() ) {
					if( kid.getNodeType() == Node.TEXT_NODE  ){
						return kid.getNodeValue();
                    }
                }
            }
        }
        return "";
    }
}
